﻿using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
namespace BOMS.Lib.Net
{
    /// <summary> 
    /// 网络通讯事件模型委托 
    /// </summary> 
    public delegate void NetEvent(object sender, NetEventArgs e);
    ///-----------------------------------------------------------------*
    /// <summary>
    /// TCP服务器类
    /// </summary>
    ///-----------------------------------------------------------------*
    ///
    /// <summary> 
    /// 通讯编码格式提供者,为通讯服务提供编码和解码服务 
    /// 你可以在继承类中定制自己的编码方式如:数据加密传输等 
    /// </summary> 
    public class Coder
    {
        /// <summary> 
        /// 编码方式 
        /// </summary> 
        private EncodingMothord _encodingMothord;

        protected Coder()
        {

        }

        public Coder(EncodingMothord encodingMothord)
        {
            _encodingMothord = encodingMothord;
        }

        public enum EncodingMothord
        {
            Default = 0,
            Unicode,
            UTF8,
            ASCII,
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 通讯数据解码 
        /// </summary> 
        /// <param name="dataBytes">需要解码的数据</param> 
        /// <returns>编码后的数据</returns> 
        ///-----------------------------------------------------------------*
        public virtual string GetEncodingString(byte[] dataBytes, int size)
        {
            switch (_encodingMothord)
            {
                case EncodingMothord.Default:
                    {
                        return Encoding.Default.GetString(dataBytes, 0, size);
                    }
                case EncodingMothord.Unicode:
                    {
                        return Encoding.Unicode.GetString(dataBytes, 0, size);
                    }
                case EncodingMothord.UTF8:
                    {
                        return Encoding.UTF8.GetString(dataBytes, 0, size);
                    }
                case EncodingMothord.ASCII:
                    {
                        return Encoding.ASCII.GetString(dataBytes, 0, size);
                    }
                default:
                    {
                        throw (new Exception("未定义的编码格式"));
                    }
            }

        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 数据编码 
        /// </summary> 
        /// <param name="datagram">需要编码的报文</param> 
        /// <returns>编码后的数据</returns> 
        ///-----------------------------------------------------------------*
        public virtual byte[] GetEncodingBytes(string datagram)
        {
            switch (_encodingMothord)
            {
                case EncodingMothord.Default:
                    {
                        return Encoding.Default.GetBytes(datagram);
                    }
                case EncodingMothord.Unicode:
                    {
                        return Encoding.Unicode.GetBytes(datagram);
                    }
                case EncodingMothord.UTF8:
                    {
                        return Encoding.UTF8.GetBytes(datagram);
                    }
                case EncodingMothord.ASCII:
                    {
                        return Encoding.ASCII.GetBytes(datagram);
                    }
                default:
                    {
                        throw (new Exception("未定义的编码格式"));
                    }
            }
        }

    }

    ///-----------------------------------------------------------------*
    /// <summary> 
    /// 数据报文分析器,通过分析接收到的原始数据,得到完整的数据报文. 
    /// 继承该类可以实现自己的报文解析方法. 
    /// 通常的报文识别方法包括:固定长度,长度标记,标记符等方法 
    /// 本类的现实的是标记符的方法,你可以在继承类中实现其他的方法 
    /// </summary> 
    ///-----------------------------------------------------------------*
    public class DatagramResolver
    {
        /// <summary> 
        /// 报文结束标记 
        /// </summary> 
        private string endTag;

        /// <summary> 
        /// 返回结束标记 
        /// </summary> 
        string EndTag
        {
            get
            {
                return endTag;
            }
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 受保护的默认构造函数,提供给继承类使用 
        /// </summary> 
        ///-----------------------------------------------------------------*
        protected DatagramResolver()
        {

        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="endTag">报文结束标记</param> 
        ///-----------------------------------------------------------------*
        public DatagramResolver(string endTag)
        {
            if (endTag == null)
            {
                throw (new ArgumentNullException("结束标记不能为null"));
            }

            if (endTag == "")
            {
                throw (new ArgumentException("结束标记符号不能为空字符串"));
            }

            this.endTag = endTag;
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 解析报文 
        /// </summary> 
        /// <param name="rawDatagram">原始数据,返回未使用的报文片断, 
        /// 该片断会保存在Session的Datagram对象中</param> 
        /// <returns>报文数组,原始数据可能包含多个报文</returns> 
        ///-----------------------------------------------------------------*
        public virtual string[] Resolve(ref string rawDatagram)
        {
            ArrayList datagrams = new ArrayList();

            //末尾标记位置索引 
            int tagIndex = -1;

            while (true)
            {
                tagIndex = rawDatagram.IndexOf(endTag, tagIndex + 1);

                if (tagIndex == -1)
                {
                    break;
                }
                else
                {
                    //按照末尾标记把字符串分为左右两个部分 
                    string newDatagram = rawDatagram.Substring
                        (0, tagIndex + endTag.Length);

                    datagrams.Add(newDatagram);

                    if (tagIndex + endTag.Length >= rawDatagram.Length)
                    {
                        rawDatagram = "";

                        break;
                    }
                    rawDatagram = rawDatagram.Substring
                        (tagIndex + endTag.Length,
                        rawDatagram.Length - newDatagram.Length);

                    //从开始位置开始查找 
                    tagIndex = 0;
                }
            }

            string[] results = new string[datagrams.Count];

            datagrams.CopyTo(results);

            return results;
        }

    }

    ///-----------------------------------------------------------------*
    /// <summary> 
    /// 客户端与服务器之间的会话类 
    /// 
    /// 版本: 1.1 
    /// 替换版本: 1.0 
    /// 
    /// 说明: 
    /// 会话类包含远程通讯端的状态,这些状态包括Socket,报文内容, 
    /// 客户端退出的类型(正常关闭,强制退出两种类型) 
    /// </summary> 
    ///-----------------------------------------------------------------*
    public class Session : ICloneable
    {
        #region 字段

        /// <summary> 
        /// 会话ID 
        /// </summary> 
        private SessionId _id;

        /// <summary> 
        /// 客户端发送到服务器的报文 
        /// 注意:在有些情况下报文可能只是报文的片断而不完整 
        /// </summary> 
        private string _datagram;

        /// <summary> 
        /// 客户端的Socket 
        /// </summary> 
        private Socket _cliSock;

        /// <summary> 
        /// 客户端的退出类型 
        /// </summary> 
        private ExitType _exitType;

        /// <summary> 
        /// 退出类型枚举 
        /// </summary> 
        public enum ExitType
        {
            NormalExit,
            ExceptionExit
        };

        private List<ByteArrayInfo> _ByteArrayList=new List<ByteArrayInfo>();
       

        #endregion

        #region 属性

        /// <summary> 
        /// 返回会话的ID 
        /// </summary> 
        public SessionId ID
        {
            get
            {
                return _id;
            }
        }

        /// <summary> 
        /// 存取会话的报文 
        /// </summary> 
        public string Datagram
        {
            get
            {
                return _datagram;
            }
            set
            {
                _datagram = value;
            }
        }

        /// <summary> 
        /// 获得与客户端会话关联的Socket对象 
        /// </summary> 
        public Socket ClientSocket
        {
            get
            {
                return _cliSock;
            }
        }

        /// <summary> 
        /// 存取客户端的退出方式 
        /// </summary> 
        public ExitType TypeOfExit
        {
            get
            {
                return _exitType;
            }

            set
            {
                _exitType = value;
            }
        }
        /// <summary>
        /// 字节数组的列表
        /// </summary>
        public List<ByteArrayInfo> ByteArrayList
        {
            get
            {
                return _ByteArrayList;
            }
        }
        #endregion

        #region 方法
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 使用Socket对象的Handle值作为HashCode,它具有良好的线性特征. 
        /// </summary> 
        /// <returns>返回句柄</returns> 
        ///-----------------------------------------------------------------*
        public override int GetHashCode()
        {
            return (int)_cliSock.Handle;
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 返回两个Session是否代表同一个客户端 
        /// </summary> 
        /// <param name="obj"></param> 
        /// <returns>返回句柄</returns> 
        ///-----------------------------------------------------------------*
        public override bool Equals(object obj)
        {
            Session rightObj = (Session)obj;

            return (int)_cliSock.Handle == (int)rightObj.ClientSocket.Handle;

        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 重载ToString()方法,返回Session对象的特征 
        /// </summary> 
        /// <returns></returns> 
        ///-----------------------------------------------------------------*
        public override string ToString()
        {
            string result = string.Format
            ("Session:{0},IP:{1}", _id, _cliSock.RemoteEndPoint.ToString());
            return result;
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="cliSock">会话使用的Socket连接</param> 
        ///-----------------------------------------------------------------*
        public Session(Socket cliSock)
        {
            Debug.Assert(cliSock != null);

            _cliSock = cliSock;

            _id = new SessionId((int)cliSock.Handle);
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 关闭会话 
        /// </summary> 
        ///-----------------------------------------------------------------*
        public void Close()
        {
            Debug.Assert(_cliSock != null);

            //关闭数据的接受和发送 
            _cliSock.Shutdown(SocketShutdown.Both);

            //清理资源 
            _cliSock.Close();
        }

        #endregion

        #region ICloneable 成员

        object System.ICloneable.Clone()
        {
            Session newSession = new Session(_cliSock);
            newSession.Datagram = _datagram;
            newSession.TypeOfExit = _exitType;
            for (int i = 0; i < _ByteArrayList.Count; i++)
            {
                newSession.ByteArrayList.Add((ByteArrayInfo)_ByteArrayList[i].Clone());
            }

            return newSession;
        }

        #endregion
    }


    /// <summary> 
    /// 唯一的标志一个Session,辅助Session对象在Hash表中完成特定功能 
    /// </summary> 
    public class SessionId
    {
        /// <summary> 
        /// 与Session对象的Socket对象的Handle值相同,必须用这个值来初始化它 
        /// </summary> 
        private int _id;

        /// <summary> 
        /// 返回ID值 
        /// </summary> 
        public int ID
        {
            get
            {
                return _id;
            }
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="id">Socket的Handle值</param> 
        ///-----------------------------------------------------------------*
        public SessionId(int id)
        {
            _id = id;
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 重载.为了符合Hashtable键值特征 
        /// </summary> 
        /// <param name="obj"></param> 
        /// <returns></returns>
        ///-----------------------------------------------------------------*
        public override bool Equals(object obj)
        {
            if (obj != null)
            {
                SessionId right = (SessionId)obj;

                return _id == right._id;
            }
            else if (this == null)
            {
                return true;
            }
            else
            {
                return false;
            }

        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 重载.为了符合Hashtable键值特征 
        /// </summary> 
        /// <returns></returns> 
        ///-----------------------------------------------------------------*
        public override int GetHashCode()
        {
            return _id;
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 重载,为了方便显示输出 
        /// </summary> 
        /// <returns></returns> 
        ///-----------------------------------------------------------------*
        public override string ToString()
        {
            return _id.ToString();
        }

    }

    ///-----------------------------------------------------------------*
    /// <summary> 
    /// 服务器程序的事件参数,包含了激发该事件的会话对象 
    /// </summary> 
    ///-----------------------------------------------------------------*
    public class NetEventArgs : EventArgs
    {

        #region 字段

        /// <summary> 
        /// 客户端与服务器之间的会话 
        /// </summary> 
        private Session _client;

        #endregion

        #region 构造函数
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="client">客户端会话</param> 
        ///-----------------------------------------------------------------*
        public NetEventArgs(Session client)
        {
            if (null == client)
            {
                throw (new ArgumentNullException());
            }

            _client = client;
        }
        #endregion

        #region 属性

        /// <summary> 
        /// 获得激发该事件的会话对象 
        /// </summary> 
        public Session Client
        {
            get
            {
                return _client;
            }

        }

        #endregion

    }
    
    /// <summary>
    /// 字节数组信息
    /// </summary>
    public class ByteArrayInfo:ICloneable
    {
        public Byte[] HexArray { get; set; }
        public int DataLength { get; set; }
        public Byte PackPrefix { get; set; }

        public object Clone()
        {
            ByteArrayInfo newobj = new ByteArrayInfo();
            newobj.HexArray = (Byte[])HexArray.Clone();
            newobj.DataLength = DataLength;
            newobj.PackPrefix = PackPrefix;
            return newobj;
        }
    }
}
